package moa.sequencestreams;

import java.util.Random;
import org.apache.commons.math.distribution.PoissonDistributionImpl;
import org.apache.commons.math.random.RandomDataImpl;

import weka.core.Attribute;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;
import moa.core.InputStreamProgressMonitor;
import moa.core.InstancesHeader;
import moa.core.ObjectRepository;
import moa.options.AbstractOptionHandler;
import moa.options.FloatOption;
import moa.options.IntOption;
import moa.streams.InstanceStream;
import moa.tasks.TaskMonitor;

import moa.sequencestreams.generator.ArrayString;
import moa.sequencestreams.generator.CustSeq;
import moa.sequencestreams.generator.PatternPar;
//import moa.sequencestreams.generator.SeqPar;
import moa.sequencestreams.generator.StringSet;
import moa.sequencestreams.generator.StringSetIter;

public class IBM_Sequence_Generator extends AbstractOptionHandler implements
InstanceSequenceStream {

	public IntOption nbCustomerOption = new IntOption("nbCustomer",
			'n', "Number of different customers.", 5000);

	public FloatOption nbPurchaseOption = new FloatOption(
			"nbPurchase", 'p',
			"AVG number of purchase by Transaction.", 3);
	
	public FloatOption nbTransactionOption = new FloatOption(
			"nbTransaction", 't',
			"AVG number of Transaction of customers.", 10);

	public IntOption nbItemsOption = new IntOption(
			"nbItems", 'i',
			"Number of different items.",10000);
	
	public FloatOption reptOption = new FloatOption(
			"rept", 'r',
			"AVG number of purchase by Transaction.", 0);
	
	public FloatOption reptVarOption = new FloatOption(
			"reptVar", 'v',
			"AVG number of Transaction of customers.", 0.1);
	
	// number of patterns int 
	public IntOption nbPatLitsOption = new IntOption("nbPatLits",
			'a', "Number of different customers.", 25000);
	
	public IntOption nbPatLseqOption = new IntOption("nbPatLseq",
			'b', "Number of different customers.", 5000);
	// average length of pattern float
	public FloatOption patlenLitsOption = new FloatOption(
			"patlenLits", 'c',
			"AVG number of Transaction of customers.", 1.5);	
	
	public FloatOption patlenLseqOption = new FloatOption(
			"patlenLseq", 'd',
			"AVG number of Transaction of customers.", 4);	
	// correlation between consecutive patterns float
	public FloatOption corrLitsOption = new FloatOption(
			"corrLits", 'e',
			"AVG number of Transaction of customers.", 0.25);	
	
	public FloatOption corrLseqOption = new FloatOption(
			"corrLseq", 'f',
			"AVG number of Transaction of customers.", 0.25);	

	// average confidence in a rule
	public FloatOption confLitsOption = new FloatOption(
			"confLits", 'g',
			"AVG number of Transaction of customers.", 0.75);	
	
	public FloatOption confLseqOption = new FloatOption(
			"confLseq", 'h',
			"AVG number of Transaction of customers.", 0.75);		
	
	
	// variation in the confidence
	public FloatOption conf_varLitsOption = new FloatOption(
			"conf_varLits", 'j',
			"AVG number of Transaction of customers.", 0.1);	
	
	public FloatOption conf_varLseqOption = new FloatOption(
			"conf_varLseq", 'k',
			"AVG number of Transaction of customers.", 0.1);		
	
	
	
	
	protected InstancesHeader streamHeader;
	
	
	Attribute atr;

	private Random instanceRandom;
	
	private int cpt = 0;
	private int cptCust = 0;
	// potentially large sequences
	private  StringSet lseq;	
	private  StringSetIter patterns;
	// potentially large itemsets
	private  StringSet lits;		
	private  CustSeq cust;	 
	private long slen;
	private long tlen;
	private RandomDataImpl rd;

	
	@Override
	protected void prepareForUseImpl(TaskMonitor monitor, ObjectRepository repository) {
		monitor.setCurrentActivity("Preparing random sequence values...", -1.0);
		
		// number of customers 
		int ncust = nbCustomerOption.getValue(); 
		// average sequence length
		float slength =(float) nbTransactionOption.getValue(); 
		// average transaction length
		float tlength = (float) nbPurchaseOption.getValue(); 
		 // number of items
		int nitems = nbItemsOption.getValue();
		
		float rept= (float) reptOption.getValue();
		float rept_var=(float) reptVarOption.getValue(); 

		// parameters for potentially large itemsets
		//PatternPar parlits = new PatternPar();	
		PatternPar parlits = new PatternPar(
				nbPatLitsOption.getValue(),
				(float)patlenLitsOption.getValue(),
				(float)corrLitsOption.getValue(),
				(float)confLitsOption.getValue(),
				(float)conf_varLitsOption.getValue());
		// parameters for potentially large sequences
		//PatternPar parlseq = new PatternPar();	
		PatternPar parlseq = new PatternPar(
				nbPatLseqOption.getValue(),
				(float)patlenLseqOption.getValue(),
				(float)corrLseqOption.getValue(),
				(float)confLseqOption.getValue(),
				(float)conf_varLseqOption.getValue());

		// Number of transactions
		rd = new RandomDataImpl(); // Math library 
		slen = rd.nextPoisson(slength-1);
		tlen = rd.nextPoisson(tlength-1);
		  
		// Table of larges itemsets
		lits = new StringSet(nitems,parlits, null, parlits.conf, parlits.conf_var);
		  
		// Table of larges sequences 
		lseq = new StringSet(parlits.getNpats(), parlseq, null, rept, rept_var);

		patterns = new StringSetIter(lseq);	
		
		generateHeader();
		restart();	
	}

	private void generateHeader() {
		// TODO Auto-generated method stub
		FastVector attributes = new FastVector();
		FastVector classLabels = null;
		atr = new Attribute("String",classLabels);
		attributes.addElement(atr);
		this.streamHeader = new InstancesHeader(new Instances(
				getCLICreationString(InstanceStream.class), attributes, 0));
		this.streamHeader.setClassIndex(this.streamHeader.numAttributes() - 1);
	}

	@Override
	public long estimatedRemainingInstances() {
		//System.out.println("**" + (nbCustomerOption.getValue() - cpt));
		return (nbCustomerOption.getValue() - cpt);
	}

	@Override
	public InstancesHeader getHeader() {
		return this.streamHeader;
	}

	@Override
	public boolean hasMoreInstances() {
		if (cpt < nbCustomerOption.getValue())
		{
			cpt ++;
			return true;
		}
		else	
			return false;
	}

	@Override
	public boolean isRestartable() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Instance nextInstance() {
		// Each instance represent one customer
		InstancesHeader header = getHeader();
		Instance inst = new Instance(header.numAttributes());
		String randValue = "";
		//System.out.println("\n Customer => " + cptCust);
		cust =  mk_seq(cptCust+1, patterns, lits,(int) slen+1,(int) tlen+1);
		String sequence;
		sequence = cust.write();
		//System.out.println("-->" + sequence);
			
		  inst.setValue(atr, sequence );
		  inst.setDataset(header);
		  return inst;
	}
	
	// Generate a customer-sequence
	public static CustSeq mk_seq(int cid,				// customer-id
					StringSetIter lseq,	// table of large sequences
					StringSet lits,		// table of large itemsets
					int slen,				// sequence length
					int tlen				// avg. transaction length
					){

		ArrayString pat;
	  CustSeq cust = new CustSeq(cid, slen, tlen);
	  while (cust.size() < slen * tlen) {
		  pat = lseq.get_pat(); 
		    if (cust.add(pat, lits) == false){ // transaction full
			    lseq.unget_pat();
			    break;
		    }
	    }
	  return cust;
	}
	

	@Override
	public void restart() {
		// TODO Auto-generated method stub
	}

	@Override
	public void getDescription(StringBuilder sb, int indent) {
		// TODO Auto-generated method stub
		
	}

}
